Access to Traps
Volume Number: 1
Issue Number: 6
Column Tag: MacPascal
Access to OS traps
By Alan Wootton
MacPascal is a miracle. Although billed as an educational product, and many
serious programmers probably regard it as a toy (just as many consider the Mac to be
merely a toy), MacPascal can be used to prototype professional software products.
Since MacPascal is not sold and supported as a development tool, a small amount
of homework is needed to assemble a useful system. First you will need MacPascal.
Second you will need RMaker, the resource compiler that runs on the Mac. For some an
assembler is nice. And a 68000 debugger may be needed (I use MacDB by Bill Duvall).
Since there is no syntax check on inline procedures, a debugger is useful to check the
stack at the start of the call even if you are relatively bad at assembly language.
Eventually one needs a compiler. For now all that is available is the Lisa Pascal
Workshop. Hopefully, a Mac compiler will become available. For my purposes it is
necessary for the compiler to generate native code; this rules out a p-code type of
system (SoftTech Pascal won’t do). Last but not least, Inside Macintosh is a must.
MacPascal provides methods to access almost all of the toolbox routines that make
the Mac so great. The OS traps, however, are a bit tricky. As Steve Brecher showed us
last month in MacTutor with his Advanced Mac’ing column, the undocumented GENERIC
procedure is used to call OS traps. However, INLINE can also be used for OS traps, as
we shall see this month in this example of using STUFFHEX from Pascal. (See Steve’s
Advanced Mac’ing column in this month’s issue for an example of using STUFFHEX from
Basic.)
TWO TYPES OF TOOLBOX TRAPS
There are two types of toolbox trap; those that are stack based and those that are
register based. The stack based calls are supported using the INLINE procedure. The
register based traps, of which most of the OS traps are typical, are more difficult to
access because there is no way (other than using GENERIC) to set registers from
Pascal. Fortunately, virtually all of the register traps use only D0 and A0. Therefore,
if we could set D0 and A0 before the trap and read them afterwards, we would have an
OS trap call mechanism using the INLINE procedure. I have written a short piece of
68000 code to accomplish this.
As an example, I have prepared a short program to read the date and time from
the system. Two calls are made to the toolbox to do this; both are register based. Note
that when a pointer is to be passed in A0, the @ operator must be used to set the
variable to the correct value before the call. Also note that for any $A0xx calls that
return a value to the calling routine, the flag $100 should be added to not preserve all
registers.
GLUE ROUTINE FOR PASCAL TO OS
Our machine language routine will pop from the stack, the addresses of the
variables which we wish to place in the registers A0 and D0. We will pop these
addresses off the stack and use them to load the variables into the two registers. The
new values returned in A0 and D0 will then be copied back into those same two Pascal
variables. Note that after the inlineP call, all registers are saved so we can use them
any way we please. To return to Pascal, we are using an RTS instruction so it will be
necessary for our machine language routine to calculate our return address.
Fortunately A0 points at the word before where we must return.
ROUTINE TO LOAD A0 AND D0 FOR OS TRAP CALLS FROM MACPASCAL.
126: 2848 MOVE.L A0,A4 ;SAVE RETURN
128: 548C ADDQ.L #$2,A4 ;POINT TO RET
;
; get address where trap will go into A0
;
12A: 41FA 000C LEA *+$000E,A0
;
; pop trap from stack and place into code
;
12E: 309F MOVE.W (A7)+,(A0)
;
; get A0 and D0 from stack
;
130: 245F MOVE.L (A7)+,A2 ;POP A0 VAR
132: 265F MOVE.L (A7)+,A3 ;POP D0 VAR
134: 2052 MOVE.L (A2),A0 ;LOAD A0
136: 2013 MOVE.L (A3),D0 ;LOAD D0
;
; this word gets overwritten by real trap
;
138: FFFF .word $FFFF ;do trap now
;
; pass back A0 and D0 contents
;
13A: 2488 MOVE.L A0,(A2) ;SAVE RET A0
13C: 2680 MOVE.L D0,(A3) ;SAVE RET D0
13E: 4ED4 JMP (A4) ;RETURN
We will stuff the machine code for our little OS trap caller above into memory by
using the STUFFHEX routine from our Pascal program. The above listing is provided to
show how the machine language patch works. Two OS traps, ReadDateTime and
Secs2Date will be called using our OS trap caller, returning the information in
variables of our DateTime record. From this example, it should be obvious how to
access other OS type traps using INLINE procedures. (As we mentioned, the
undocumented GENERIC can also be used for this purpose.)
program show_date_time;
{ This is a test of a method of accessing }
{ the Mac OS toolbox traps from Mac }
{ Pascal that are register based, using }
{ A0 and D0 only. The call will be: }
{inlineP($4E75,@d0, @a0, trap,@access)}
{ d0 and a0 hold values of registers D0 }
{ and A0 before and after the call. Trap }
{ refers to the trap address being called.}
DaTimRec = record
year: integer;
month: integer;
day: integer;
hour: integer;
minute: integer;
second: integer;
dayofweek: integer;
end;
var
currentTime: DatimRec;
procedure GetTime(var date: DaTimRec);
var
access: array[0..12] of integer;
d0,a0, secs: longint;
begin
stuffhex(@access,
’2848548C41FA000C309F245F265F20522013FFFF248826804ED4');
a0:=ord(@secs);
inlineP($4E75,@d0,@a0,$A139, @access);
{ReadDateTime, pointer to secs in A0}
a0:=ord(@date);
inlineP($4E75,@secs,@a0,$A9C6,@access);
{Secs2Date, secs in d0, pointer to record}
{is in A0.}
end;
begin {main}
GetTime( currentTime);
with currentTime do
begin
writeln(‘year is’, year);
writeln(‘month is’, month);
writeln(‘day is’, day);
writeln(‘hour is’, hour);
writeln(‘minute is’, minute);
writeln(‘second is’, second);
writeln(‘year is’, year);
writeln(‘day of week is’, dayofweek);
end;
end.